// ?：匹配不捕获
// arguments[0] 匹配到的标签
// arguments[1] 匹配到的标签名字
// 字母a-zA-Z_ - . 数组小写字母 大写字母
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // 标签名
// ?:匹配不捕获   <aaa:aaa>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// startTagOpen 可以匹配到开始标签 正则捕获到的内容是 (标签名)
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
// 闭合标签 </xxxxxxx>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 </div>
// <div aa   =   "123"  bb=123  cc='123'
// 捕获到的是 属性名 和 属性值 arguments[1] || arguments[2] || arguments[2]
const attribute =
  /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的
// <div >   <br/>
const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
// 匹配动态变量的  +? 尽可能少匹配
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;

// ast (语法层面的描述  不仅可以针对js css，也可以针对html)  vdom （仅仅能够处理dom）
// html字符串解析成 对应的脚本触发  tokens  <div id="app"> {{name}} </div>
// 将解析后的结果，组成一个树结构  --> 栈结构实现

export function parserHTML(html) {
  function createASTElement(tagName, attrs) {
    return {
      tag: tagName,
      type: 1,
      children: [],
      parent: null,
      attrs,
    };
  }
  let stack = [];
  let root = null;
  function advance(len) {
    html = html.substring(len);
  }

  function parseStartTag() {
    const start = html.match(startTagOpen);
    // console.log(start);
    if (start) {
      const match = {
        tagName: start[1],
        attrs: [],
      };
      advance(start[0].length); // 截取已处理的字符
      let end;
      // 如果没有遇到标签结尾就不停的进行解析
      let attr;
      while (
        !(end = html.match(startTagClose)) &&
        (attr = html.match(attribute))
      ) {
        // console.log(attr);
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5], // 双引号、单引号、啥都没
        });
        advance(attr[0].length);
      }
      // console.log(match);
      // console.log(html);
      if (end) {
        advance(end[0].length);
      }
      return match;
    }
    return false;
  }

  function start(tagName, attribute) {
    let parent = stack[stack.length - 1];
    let element = createASTElement(tagName, attribute);
    if (!root) {
      root = element;
    }
    if (parent) {
      element.parent = parent; // 当放入栈中是， 继承父亲是谁

      parent.children.push(element);
    }
    stack.push(element);
    // console.log("start:", tagName, attribute);
  }

  function end(tagName) {
    let last = stack.pop();
    if (last.tag !== tagName) {
      throw new Error("标签有误");
    }

    // console.log("end:", tagName);
  }

  function chars(text) {
    text = text.replace(/\s/g, "");
    let parent = stack[stack.length - 1];
    if (text) {
      parent.children.push({
        type: 3,
        text,
      });
    }
    // console.log("text:", text);
  }

  while (html) {
    // 看解析的内容是否存在， 如果存在就不停的解析  htmlParse2（现成的解析库）
    let textEnd = html.indexOf("<"); // 当前解析的开头
    if (textEnd == 0) {
      const startTagMatch = parseStartTag(html); // 解析开始标签

      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }

      const endTagMatch = html.match(endTag);
      // const endTagMatch = parseEndTag(); // 解析结束标签
      if (endTagMatch) {
        end(endTagMatch[1]);
        advance(endTagMatch[0].length);
        continue;
      }
    }

    let text; // 12123</div>
    if (textEnd > 0) {
      text = html.substring(0, textEnd);
    }
    if (text) {
      chars(text);
      advance(text.length);
    }
  }

  return root;
}

// 看一下用户是否传入了render函数，
// 没传入可能传入了template
// 如果template也没有传递，将我们的html --> 词法解析 (开始标签、属性、结束标签、 文本)
// --> ast语法树 用来描述html语法  stack=[]
// --> codegen  <div>hello</div>   ==> _c("div", {}, "hello") ==> 让字符串执行
// --> 字符串如果转成代码  eval 非常耗性能， 会有作用域问题  ==>  模板引擎  new Function  + with 实现
